package cn.com.bluemoon.daps.standard.excel.easypoi;

import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler;
import cn.com.bluemoon.daps.common.enums.StandardDataType;
import cn.com.bluemoon.daps.common.validation.JValidUtils;
import cn.com.bluemoon.daps.standard.client.DapModelModuleFacade;
import cn.com.bluemoon.daps.standard.dto.ExcelImportGroup;
import cn.com.bluemoon.daps.standard.entity.DapStandardIndex;
import cn.com.bluemoon.daps.standard.entity.DapSystemCategory;
import cn.com.bluemoon.daps.standard.vo.IndexStandardRelateDwModelVo;
import cn.com.bluemoon.daps.standard.vo.IndexStandardVo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import reactor.util.function.Tuple2;

import java.io.Closeable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Jarod.Kong
 */
@Slf4j
public class IndStdImportVerifyHandler implements IExcelVerifyHandler<IndexStandardVo>, Closeable {
    /**
     * 有序的中文名称
     */
    static Set<String> hadReadStandardNames = Sets.newHashSet();
    /**
     * 有序的英文名称
     */
    static Set<String> hadReadStandardEngNames = Sets.newHashSet();
    private final Map<String, String> dbDepartAndIdMap;
    private final Set<String> dbPublishedStandardNames;
    private final Set<String> dbPublishedStandardEngNames;
    private final List<DapSystemCategory> systemTopics, systemCategories;
    private Map<String, Integer> bizLineAndIdMap;
    private Map<String, String> dimNameAndIdMap;
    @Setter
    private Map<String, String> modelNameAndIdMap = new HashMap<>();

    @Setter
    private DapModelModuleFacade modelModuleFacade;

    private Map<String, String> dbDataSourceSystemMap = Maps.newHashMap();
    private Map<String, String> dbPublishedStandardNameAndStdUnqueIds = Maps.newHashMap();

    public IndStdImportVerifyHandler(List<DapSystemCategory> systemTopics,
                                     List<DapSystemCategory> systemCategories,
                                     Map<String, String> dbDepartAndIdMap,
                                     Set<String> dbPublishedStandardNames,
                                     Set<String> dbPublishedStandardEngNames) {
        this.systemTopics = Optional.ofNullable(systemTopics).orElseGet(Lists::newArrayList);
        this.systemCategories = Optional.ofNullable(systemCategories).orElseGet(Lists::newArrayList);
        this.dbDepartAndIdMap = Optional.ofNullable(dbDepartAndIdMap).orElseGet(Maps::newHashMap);
        this.dbPublishedStandardNames = Optional.ofNullable(dbPublishedStandardNames).orElseGet(Sets::newHashSet);
        this.dbPublishedStandardEngNames = Optional.ofNullable(dbPublishedStandardEngNames).orElseGet(Sets::newHashSet);
    }

    /**
     * 导入校验方法
     *
     * @param obj 当前对象
     */
    @Override
    public ExcelVerifyHandlerResult verifyHandler(IndexStandardVo obj) {
        // 与数据库已发布名称校验
        List<String> errors = Lists.newArrayList();
        if (dbPublishedStandardNames.contains(obj.getIndexChinName())) {
            errors.add("[中文名称]列值，数据库中已存在");
        }
        if (dbPublishedStandardEngNames.contains(obj.getIndexEngName())) {
            errors.add("[英文名称]列值，数据库中已存在");
        }
        // 提前检查是否在同一个Excel中存在多条一样的中英文数据
        if (hadReadStandardEngNames.contains(obj.getIndexEngName())) {
            errors.add("[英文名称]列值，存在重复");
        } else {
            hadReadStandardEngNames.add(obj.getIndexEngName());
        }
        if (hadReadStandardNames.contains(obj.getIndexChinName())) {
            errors.add("[中文名称]列值，存在重复");
        } else {
            hadReadStandardNames.add(obj.getIndexEngName());
        }
        // 校验嵌套表
        if (dbDepartAndIdMap.get(obj.getDepartmentName()) != null) {
            obj.setManagementDepartmentId(dbDepartAndIdMap.get(obj.getDepartmentName()));
        } else {
            errors.add("[*归口管理部门]列，无效");
        }
        // 转换业务线
        Map<String, Integer> bizLineAndIdMap = getBizLineAndIdMap();
        if (obj.getBizLine() != null && !bizLineAndIdMap.isEmpty()) {
            Integer bizLine = bizLineAndIdMap.getOrDefault(obj.getBizLine(), null);
            if (bizLine == null) {
                errors.add("[*业务线]列，无效");
            }
            obj.setBizLineId(bizLine);
        } else {
            errors.add("[*业务线]列，必填");
        }
        // 多表头集合 回填到 且判断是否成功
        obj.getDataStandardDtos().stream().limit(1).forEach(d -> {
            List<String> valid = JValidUtils.INSTANCE.setGroupClzs(new Class[]{ExcelImportGroup.class}).validate(d);
            if (!valid.isEmpty()) {
                errors.add(String.format("[数据规范]数据校验失败，原因：%s", String.join("；", valid)));
            }
            BeanUtils.copyProperties(d, obj);
            // 校验 数据类型
            String indexFormat = d.getIndexFormat();
            try {
                StandardDataType standardDataType = StandardDataType.valueOf(indexFormat);
                obj.setIndexFormat(standardDataType);
            } catch (Exception e) {
                log.error("[指标数据格式]列，内容不符合，目前支持[金额类或数值类]");
                indexFormat = null;
            }
            if (StringUtils.isBlank(indexFormat)) {
                errors.add("[指标数据格式]列，内容不符合，目前支持[金额类或数值类]");
            }
            // 校验长度
            if (StringUtils.isNotBlank(d.getDateLength())) {
                String[] errorStartLeft = {"-", "(-", "（-"};
                String dataLength = d.getDateLength().trim();
                boolean matchError = Arrays.stream(errorStartLeft).anyMatch(dataLength::startsWith);
                if (matchError) {
                    errors.add("[数据长度]列不可为负数");
                }
            }
            obj.setDateLength(d.getDateLength());
            String dimNamesStr = d.getAssociationDimensionNames();
            if (StringUtils.isNotBlank(dimNamesStr)) {
                String[] dimNames = dimNamesStr.split(",|，");
                Map<String, String> dimNameAndIdMap = getDimNameAndIdMap();
                String dimIds = Arrays.stream(dimNames).filter(dimNameAndIdMap::containsKey)
                        .map(dimNameAndIdMap::get).collect(Collectors.joining(","));
                obj.setAssociationDimension(dimIds);
            }
            // 关联指标（已发布） 标准中文名称 => standardUniqueId
            String relatedIndex = obj.getRelatedIndex();
            if (StringUtils.isNotBlank(relatedIndex)) {
                String[] reletedIndexes = relatedIndex.split(",|，");
                String relatedIndexIds = Arrays.stream(reletedIndexes).map(r -> dbPublishedStandardNameAndStdUnqueIds.getOrDefault(r, null))
                        .filter(StringUtils::isNotBlank).collect(Collectors.joining(","));
                obj.setRelatedIndex(relatedIndexIds);
            }
            // 2021年5月25日12:02:54 新增需求：增加指标类型属性：原子指标、派生指标、计算指标； 影响属性：关联指标，原子指标没有关联指标
            Optional<DapStandardIndex.StdIndexType> stdIndexType = Arrays.stream(DapStandardIndex.StdIndexType.values())
                    .filter(i -> i.getName().equals(d.getIndexTypeStr())).findFirst();
            if (stdIndexType.isPresent()) {
                if (stdIndexType.get().getType() == 1 && StringUtils.isNotBlank(obj.getRelatedIndex())) {
                    errors.add("[指标类型]为原子指标，不允许关联指标");
                } else {
                    obj.setIndexType(stdIndexType.get().getType());
                }
            }
        });
        obj.getIndexCaliberDtos().stream().limit(1).forEach(d -> {
            List<String> valid = JValidUtils.INSTANCE.setGroupClzs(new Class[]{ExcelImportGroup.class}).validate(d);
            if (!valid.isEmpty()) {
                errors.add(String.format("[指标口径-确认]数据校验失败，原因：%s", String.join("；", valid)));
            }
            BeanUtils.copyProperties(d, obj);
        });
        analyseStaticCaliberDto(obj, errors);
        // 获取topicId,primaryCategory,departmentid 为空则失败
        Optional<DapSystemCategory> topicObj = systemTopics.stream().filter(o -> o.getCodeName().equalsIgnoreCase(StringUtils.trim(obj.getTopicName()))).findAny();
        if (!topicObj.isPresent()) {
            return new ExcelVerifyHandlerResult(false, String.format("第%d行：[主题]列值与系统主题分类匹配失败；%s", obj.getRowNum() + 1, String.join("；", errors)));
        }
        String topicId = topicObj.get().getId();
        obj.setTheme(topicId);
        Optional<DapSystemCategory> category1Obj = systemCategories.stream().filter(o -> o.getParentId().equals(topicId))
                .filter(o -> o.getCodeName().equalsIgnoreCase(obj.getFirstCategoryName())).findFirst();
        if (!category1Obj.isPresent()) {
            return new ExcelVerifyHandlerResult(false, String.format("第%d行：[一级分类]列值与系统主题分类匹配失败；%s", (obj.getRowNum() + 1), String.join("；", errors)));
        }
        String category1Id = category1Obj.get().getId();
        obj.setPrimarySort(category1Id);
        // 处理二/三级分类
        List<DapSystemCategory> nextNodes = category1Obj.get().getNextNodes();
        if (nextNodes != null && !nextNodes.isEmpty()) {
            DapSystemCategory category2Obj = nextNodes.stream().filter(o -> o.getCodeName().equalsIgnoreCase(StringUtils.trim(obj.getSecondCategoryName()))).findFirst().orElse(new DapSystemCategory());
            obj.setSecondarySort(category2Obj.getId());
            List<DapSystemCategory> nextNodes2 = category2Obj.getNextNodes();
            if (nextNodes2 != null && !nextNodes2.isEmpty()) {
                DapSystemCategory category3Obj = nextNodes2.stream().filter(o -> o.getCodeName().equalsIgnoreCase(StringUtils.trim(obj.getSecondCategoryName()))).findFirst().orElse(new DapSystemCategory());
                obj.setThirdSort(category3Obj.getId());
            }
        }
        if (!errors.isEmpty()) {
            return new ExcelVerifyHandlerResult(false, String.format("第%d行：%s", obj.getRowNum() + 1, String.join("；", errors)));
        }
        return new ExcelVerifyHandlerResult(true);
    }

    private void analyseStaticCaliberDto(IndexStandardVo obj, List<String> errors) {
        obj.getStatisticalCaliberDtos().stream().limit(1).forEach(d -> {
            List<String> valid = JValidUtils.INSTANCE.setGroupClzs(new Class[]{ExcelImportGroup.class}).validate(d);
            if (!valid.isEmpty()) {
                errors.add(String.format("[统计口径]数据校验失败，原因：%s", String.join("；", valid)));
            }
            // 数据源系统名称转为id
            if (StringUtils.isNotBlank(d.getDataSourceSystem())) {
                if (dbDataSourceSystemMap.containsKey(d.getDataSourceSystem())) {
                    d.setDataSourceSystem(dbDataSourceSystemMap.get(d.getDataSourceSystem()));
                } else {
                    errors.add("[统计口径#数据源系统]找不到对应系统");
                }
            }
            // 检查模型库表与字段id是否有效
            if (StringUtils.isBlank(d.getDataWarehouseModelName()) && StringUtils.isNotBlank(d.getDataWarehouseModel())){
                errors.add("[统计口径#数仓模型表]和[统计口径#数仓模型名]都非空或为空");
                return;
            }
            if (StringUtils.isNotBlank(d.getDataWarehouseModelName())) {
                if (!modelNameAndIdMap.containsKey(d.getDataWarehouseModelName())) {
                    errors.add("[统计口径#数仓模型名]找不到对应模型");
                    return;
                }
                final String dwTableField = d.getDataWarehouseModel();
                if (!dwTableField.contains(".")) {
                    errors.add("[统计口径#数仓模型表]必须字段表字段");
                    return;
                }
                String modelId = modelNameAndIdMap.get(d.getDataWarehouseModelName());
                if (modelModuleFacade != null) {
                    String[] tableNameAndFieldName = dwTableField.split("\\.");
                    final String tableName = tableNameAndFieldName[0], fieldName = tableNameAndFieldName[1];
                    Optional<Tuple2<String, String>> tableIdAndFieldId = modelModuleFacade.queryTableAndField(modelId, tableName, fieldName);
                    if (!tableIdAndFieldId.isPresent()) {
                        errors.add("[统计口径#数仓模型表]无法建立模型、模型表和字段关系");
                    } else {
                        Tuple2<String, String> res = tableIdAndFieldId.get();
                        IndexStandardRelateDwModelVo relateDwModelVo = new IndexStandardRelateDwModelVo();
                        relateDwModelVo.setDwModelId(modelId);
                        relateDwModelVo.setDwTableFieldId(res.getT2());
                        relateDwModelVo.setDwTableId(res.getT1());
                        obj.setIndexStandardRelateDwModelVo(relateDwModelVo);
                    }
                }
            }
            BeanUtils.copyProperties(d, obj);
        });
    }

    /**
     * 清空单Excel的缓存数据
     */
    @Override
    public void close() {
        hadReadStandardNames.clear();
        hadReadStandardEngNames.clear();
    }

    public void setDbPublishedStandardNameAndStdUnqueIds(Map<String, String> dbPublishedStandardNameAndStdUnqueIds) {
        this.dbPublishedStandardNameAndStdUnqueIds = dbPublishedStandardNameAndStdUnqueIds;
    }

    public void setDbDataSourceSystemMap(Map<String, String> dbDataSourceSystemMap) {
        this.dbDataSourceSystemMap = dbDataSourceSystemMap;
    }

    public Map<String, Integer> getBizLineAndIdMap() {
        if (bizLineAndIdMap == null) {
            return Collections.emptyMap();
        }
        return bizLineAndIdMap;
    }

    public void setBizLineAndIdMap(Map<String, Integer> bizLineAndIdMap) {
        this.bizLineAndIdMap = bizLineAndIdMap;
    }

    public Map<String, String> getDimNameAndIdMap() {
        if (dimNameAndIdMap == null) {
            return Collections.emptyMap();
        }
        return dimNameAndIdMap;
    }

    public void setDimNameAndIdMap(Map<String, String> dimNameAndIdMap) {
        this.dimNameAndIdMap = dimNameAndIdMap;
    }
}
